﻿using Devil;
using System.Collections.Generic;
using UnityEngine;

namespace GameToolkit.UI
{
    public delegate void OpenPanelResult(string uri, bool isOpened, Panel panel);

    sealed public partial class PanelManager : SingletonMono<PanelManager>
    {
        public event System.Action<Panel> OnForegroundPanelChanged
#if UNITY_EDITOR
            = (panel) =>
            {
                if (panel != null)
                    RTLog.Log(LogCat.UI, panel, $"\"{panel.name}\" become foreground.");
            }
#endif
            ;

        public event System.Action<Panel> OnOpenPanel;
        public event System.Action<Panel> OnClosePanel;

        [SerializeField]
        Camera m_UICamera;

        [SerializeField, Min(1)]
        int m_StepOrderValue = 10;

        [SerializeField, SortingLayerField]
        int m_NormalLayer;
        public int NormalSortingLayer => m_NormalLayer;

        [SerializeField]
        float m_NormalDistance = 20;
        public float NormalDistance => m_NormalDistance;

        [SerializeField, SortingLayerField]
        int m_DialogLayer;
        public int DialogSortingLayer => m_DialogLayer;

        [SerializeField]
        float m_DialogDistance = 10;
        public float DialogDistance => m_DialogDistance;

        [SerializeField, SortingLayerField]
        int m_StatusLayer;
        public int StatusSortingLayer => m_StatusLayer;

        [SerializeField]
        float m_StatusDistance = 50;
        public float StatusDistance => m_StatusDistance;

        [SerializeField, Min(4)]
        int m_PanelCapacity = 5;

        [SerializeField]
        string m_AutoUriPrefix = "Assets/Prefabs/UI/Panels";

        readonly NotIntent NO_INTENT = new NotIntent();
        List<Stub> mPanels;
        RandomList<Stub> mProgressives;
        Dictionary<int, Stub> mPanelsLib;
        List<IIntentOperation> mIntents;
        Panel mCachedForegroundPanel;
        bool mValidateFocusPanel;
        bool mOrderSortingLayers;
        public Camera UICamera => m_UICamera;
        static readonly string UriPrefix = "assets/";
        static readonly string UriSubfix = ".prefab";
        static readonly string InsidePrefix = "inside://";

        public Panel ForegroundPanel
        {
            get
            {
                for (int i = mPanels.Count - 1; i >= 0; i--)
                {
                    var p = mPanels[i].panel;
                    if (p != null && p.IsOpened)
                        return p;
                }
                return null;
            }
        }

        public void GetPanelStack(ICollection<Panel> panels, FilterDelegate<Panel> filter = null)
        {
            if (panels == null)
                return;
            for (int i = mPanels.Count - 1; i >= 0; i--)
            {
                var p = mPanels[i].panel;
                if (p != null && p.IsOpened && (filter == null || filter(p)))
                    panels.Add(p);
            }
        }

        private void Awake()
        {
            mPanels = new List<Stub>(m_PanelCapacity > 4 ? m_PanelCapacity : 4);
            mProgressives = new RandomList<Stub>();
            mPanelsLib = new Dictionary<int, Stub>();
            mIntents = new List<IIntentOperation>(8);
        }

        protected override void OnEnable()
        {
            base.OnEnable();
            if (Instance == this)
            {
                var len = transform.childCount;
                for (int i = 0; i < len; i++)
                {
                    var panel = transform.GetChild(i).GetComponent<Panel>();
                    if (panel != null)
                        RegistInsidePanel(panel);
                }
            }
        }

        private void OnDestroy()
        {
            OnForegroundPanelChanged = null;
        }

        internal void NotifyOpen(Panel panel)
        {
            OnOpenPanel?.Invoke(panel);
        }

        internal void NotifyClose(Panel panel)
        {
            OnClosePanel?.Invoke(panel);
        }

        void ExecuteIntents()
        {
            var current = mPanels.Count > 0 ? mPanels[mPanels.Count - 1] : null;
            var isDlg = current != null && current.panelType == EPanelType.Dialog;
            // execute panel intents
            var index = 0;
            while (index < mIntents.Count)
            {
                var intent = mIntents[index];
                if (!intent.stub.IsAssetReady)
                {
                    index++;
                    continue;
                }
                if (isDlg && intent.stub.panelType != EPanelType.Status)
                {
                    intent.Cancel();
                    mIntents.RemoveAt(index);

#if UNITY_EDITOR
                    Debug.LogError($"The dialog(\"{current.Uri}\") cancels the operation of opening \"{intent.stub.Uri}\".");
#endif
                    continue;
                }
                mIntents.RemoveAt(index);
                if (!intent.Execute(this))
                    continue;
                if (intent.stub.panel != null && intent.stub.panel.CurrentProgressive != Panel.EProgressive.None && !mProgressives.Contains(intent.stub))
                    mProgressives.Add(intent.stub);
                if (intent.stub.panelType != EPanelType.Status)
                {
                    var i = mPanels.IndexOf(intent.stub);
                    if (i != -1)
                        mPanels.RemoveAt(i);
                    mOrderSortingLayers |= i != mPanels.Count;
                    mPanels.Add(intent.stub);
                    mValidateFocusPanel = true;
                    current = intent.stub;
                    if (current.panelType == EPanelType.Dialog)
                    {
                        isDlg = true;
                    }
                }
            }
        }

        private void Update()
        {
            ExecuteIntents();
            if (mValidateFocusPanel)
            {
                mValidateFocusPanel = false;
                var focus = true;
                for (int i = mPanels.Count - 1; i >= 0; i--)
                {
                    var inst = mPanels[i].panel;
                    if (inst != null)
                        inst.IsFocused = focus;
                    focus = false;
                }
            }
            if (mOrderSortingLayers)
            {
                mOrderSortingLayers = false;
                var sortlayer = 1;
                for (int i = 0; i < mPanels.Count; i++)
                {
                    var intent = mPanels[i];
                    var canvas = intent.canvas;
                    if (canvas != null)
                        canvas.sortingOrder = sortlayer;
                    sortlayer += m_StepOrderValue;
                }
            }
            for (int i = mProgressives.Count - 1; i >= 0; i--)
            {
                var proc = mProgressives[i];
                if (proc.panel == null || !proc.panel.ExecuteProgressive())
                {
                    mProgressives.RemoveAt(i);
                    if (proc.mNotUsed)
                        proc.Release();
                }
            }
            var foreground = mPanels.Count > 0 ? mPanels[mPanels.Count - 1].panel : null;
            if (foreground != mCachedForegroundPanel)
            {
                mCachedForegroundPanel = foreground;
                OnForegroundPanelChanged?.Invoke(foreground);
            }
            if (foreground != null)
            {
                foreground.OnForegroundUpdate();
            }

            ProcessKeyButtons(Time.unscaledDeltaTime);
        }

        public ParallelDispatcher.Handler WarmUp(string uri)
        {
            if (!ParallelUnity.IsMainThread)
                throw new System.InvalidOperationException("Warm up panel can only be invoked from mainthread.");
            var isInside = uri.StartsWithIgnoreCase(InsidePrefix);
            if (isInside)
                return default;
            Stub stub;
            if (!string.IsNullOrEmpty(m_AutoUriPrefix) && !uri.StartsWithIgnoreCase(UriPrefix))
            {
                uri = NativeFileSystem.Combine(m_AutoUriPrefix, uri);
                if (!uri.EndsWithIgnoreCase(UriSubfix))
                    uri = ParallelUtils.Concat(uri, UriSubfix);
            }
            var id = uri.IgnoreCaseToHash();
            if (!mPanelsLib.TryGetValue(id, out stub))
            {
                stub = new Stub(uri);
                mPanelsLib[id] = stub;
                stub.GetAsset();
            }
            return stub.GetHandler();
        }

        public void RegistInsidePanel(Panel panel)
        {
            if (!ParallelUnity.IsMainThread)
                throw new System.InvalidOperationException("Regist panel can only be invoked from mainthread.");
            if (panel == null)
                throw new System.ArgumentNullException("Can't regist a panel which is not exist.");
            foreach (var item in mPanelsLib.Values)
            {
                if (item.panel == panel)
                    return;
            }
            var stub = new Stub(panel);
            var id = stub.Uri.IgnoreCaseToHash();
            Stub old;
            if (!mPanelsLib.TryGetValue(id, out old))
            {
                mPanelsLib[id] = stub;
            }
            else if (old.panel != panel)
            {
                mPanelsLib[id] = stub;
                if (mPanels.Remove(old))
                {
                    mValidateFocusPanel = true;
                    mOrderSortingLayers = true;
                }
                mProgressives.Remove(old);
                if (old.panel != null)
                    old.panel.IsOpened = false;
                old.Release();
            }
            stub.InitPanelState(this);
        }

        public void UnregistInsidePanel(Panel panel)
        {
            if (!ParallelUnity.IsMainThread)
                throw new System.InvalidOperationException("Unregist panel can only be invoked from mainthread.");
            Stub stub = null;
            int id = 0;
            foreach (var v in mPanelsLib)
            {
                var pstub = v.Value;
                if (pstub.IsInsidePanel && pstub.panel == panel)
                {
                    id = v.Key;
                    stub = pstub;
                    break;
                }
            }
            if (stub != null)
            {
                mPanelsLib.Remove(id);
                if (mPanels.Remove(stub))
                {
                    mValidateFocusPanel = true;
                    mOrderSortingLayers = true;
                }
                mProgressives.Remove(stub);
                if (stub.panel != null)
                    stub.panel.IsOpened = false;
                stub.Release();
            }
        }

        public string GetRealUri(string uri)
        {
            var isInside = uri.StartsWithIgnoreCase(InsidePrefix);
            if (!uri.StartsWithIgnoreCase(UriPrefix) && !string.IsNullOrEmpty(m_AutoUriPrefix) && !isInside)
            {
                uri = NativeFileSystem.Combine(m_AutoUriPrefix, uri);
                if (!uri.EndsWithIgnoreCase(UriSubfix))
                    uri = ParallelUtils.Concat(uri, UriSubfix);
            }
            return uri;
        }

        public Panel GetOpenedPanel(string uri)
        {
            //if (!ParallelUnity.IsMainThread)
            //    throw new System.InvalidOperationException("Open panel can only be invoked from mainthread.");
            var isInside = uri.StartsWithIgnoreCase(InsidePrefix);
            if (!uri.StartsWithIgnoreCase(UriPrefix) && !string.IsNullOrEmpty(m_AutoUriPrefix) && !isInside)
            {
                uri = NativeFileSystem.Combine(m_AutoUriPrefix, uri);
                if (!uri.EndsWithIgnoreCase(UriSubfix))
                    uri = ParallelUtils.Concat(uri, UriSubfix);
            }
            var id = uri.IgnoreCaseToHash();
            Stub stub;
            if (!mPanelsLib.TryGetValue(id, out stub))
                return null;
            return stub.panel;
        }

        public IYieldInstruction OpenPanel(string uri, OpenPanelResult handler = null)
        {
            return OpenPanel(uri, NO_INTENT, handler);
        }

        public IYieldInstruction OpenPanel<T>(string uri, T intent, OpenPanelResult handler = null)
        {
            if (!ParallelUnity.IsMainThread)
                throw new System.InvalidOperationException("Open panel can only be invoked from mainthread.");
            Stub stub;
            var isInside = uri.StartsWithIgnoreCase(InsidePrefix);
            if (!uri.StartsWithIgnoreCase(UriPrefix) && !string.IsNullOrEmpty(m_AutoUriPrefix) && !isInside)
            {
                uri = NativeFileSystem.Combine(m_AutoUriPrefix, uri);
                if (!uri.EndsWithIgnoreCase(UriSubfix))
                    uri = ParallelUtils.Concat(uri, UriSubfix);
            }
            var id = uri.IgnoreCaseToHash();
            if (!mPanelsLib.TryGetValue(id, out stub))
            {
                if (isInside)
                {
                    RTLog.LogError(LogCat.UI, $"Failed to open panel \"{uri}\". (the panel is not regist)");
                    handler?.Invoke(uri, false, null);
                    return null;
                }
                stub = new Stub(uri);
                mPanelsLib[id] = stub;
                stub.GetAsset();
            }
            var openIntent = new Intent<T>(stub, intent, handler);
            mIntents.Add(openIntent);
            return openIntent;
        }

        /// <summary>
        /// 终止执行意图
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="uri"></param>
        /// <param name="intent"></param>
        public void Terminate<T>(string uri, T intent)
        {
            if (!ParallelUnity.IsMainThread)
                throw new System.InvalidOperationException("Terminate panel can only be invoked from mainthread.");
            Stub stub;
            var isInside = uri.StartsWithIgnoreCase(InsidePrefix);
            if (!uri.StartsWithIgnoreCase(UriPrefix) && !string.IsNullOrEmpty(m_AutoUriPrefix) && !isInside)
            {
                uri = NativeFileSystem.Combine(m_AutoUriPrefix, uri);
                if (!uri.EndsWithIgnoreCase(UriSubfix))
                    uri = ParallelUtils.Concat(uri, UriSubfix);
            }
            for (int i = mIntents.Count - 1; i >= 0; i--)
            {
                var temp = mIntents[i];
                if (uri.EqualsIgnoreCase(temp.stub.Uri) && temp is Intent<T> intentOper && intentOper.IsIntent(intent))
                {
                    temp.Cancel();
                    mIntents.RemoveAt(i);
                }
            }
            var id = uri.IgnoreCaseToHash();
            if (mPanelsLib.TryGetValue(id, out stub) && stub.panel is IPanelIntentTerminate<T> terminate)
            {
                terminate.OnTerminateIntent(intent);
            }
        }

        public void Release(string uri)
        {
            ClosePanel(uri, true);
        }

        public void Release(Panel panel)
        {
            ClosePanel(panel, true);
        }

        public void ClosePanel(string uri)
        {
            ClosePanel(uri, false);
        }

        public void ClosePanel(Panel panel)
        {
            ClosePanel(panel, false);
        }

        void ClosePanel(string uri, bool release)
        {
            if (!ParallelUnity.IsMainThread)
                throw new System.InvalidOperationException("Close panel can only be invoked from mainthread.");
            if (!uri.StartsWithIgnoreCase(UriPrefix) && !string.IsNullOrEmpty(m_AutoUriPrefix) && !uri.StartsWithIgnoreCase(InsidePrefix))
            {
                uri = NativeFileSystem.Combine(m_AutoUriPrefix, uri);
                if (!uri.EndsWithIgnoreCase(UriSubfix))
                    uri = ParallelUtils.Concat(uri, UriSubfix);
            }
            for (int i = mIntents.Count - 1; i >= 0; i--)
            {
                var intent = mIntents[i];
                if (uri.EqualsIgnoreCase(intent.stub.Uri))
                {
                    intent.Cancel();
                    mIntents.RemoveAt(i);
                }
            }
            bool found = false;
            for (int i = mPanels.Count - 1; i >= 0; i--)
            {
                var stub = mPanels[i];
                if (uri.EqualsIgnoreCase(stub.Uri))
                {
                    found = true;
                    mPanels.RemoveAt(i);
                    mValidateFocusPanel = true;
                    if (stub.panel != null)
                    {
                        stub.panel.IsOpened = false;
                        if (stub.panel.CurrentProgressive != Panel.EProgressive.None && !mProgressives.Contains(stub))
                        {
                            mProgressives.Add(stub);
                        }
                    }
                    break;
                }
            }
            if (release)
            {
                Stub stub;
                var id = uri.IgnoreCaseToHash();
                if (mPanelsLib.Remove(id, out stub))
                {
                    stub.mNotUsed = true;
                    if (!found && stub.panel != null)
                    {
                        stub.panel.IsOpened = false;
                        if (stub.panel.CurrentProgressive != Panel.EProgressive.None && !mProgressives.Contains(stub))
                        {
                            mProgressives.Add(stub);
                        }
                    }
                    if (stub.panel == null || stub.panel.CurrentProgressive == Panel.EProgressive.None)
                        stub.Release();
                }
            }
            else if (!found)
            {
                Stub stub;
                var id = uri.IgnoreCaseToHash();
                if (mPanelsLib.TryGetValue(id, out stub) && stub.panel != null)
                {
                    stub.panel.IsOpened = false;
                    if (stub.panel.CurrentProgressive != Panel.EProgressive.None && !mProgressives.Contains(stub))
                    {
                        mProgressives.Add(stub);
                    }
                }
            }
        }

        void ClosePanel(Panel panel, bool release)
        {
            if (!ParallelUnity.IsMainThread)
                throw new System.InvalidOperationException("Close panel can only be invoked from mainthread.");
            if (panel == null)
                return;
            string uri = null;
            var found = false;
            for (int i = mPanels.Count - 1; i >= 0; i--)
            {
                var stub = mPanels[i];
                if (stub.panel == panel)
                {
                    found = true;
                    uri = stub.Uri;
                    mPanels.RemoveAt(i);
                    mValidateFocusPanel = true;
                    for (int k = mIntents.Count - 1; k >= 0; k--)
                    {
                        var intent = mIntents[k];
                        if (stub.Uri.EqualsIgnoreCase(intent.stub.Uri))
                        {
                            intent.Cancel();
                            mIntents.RemoveAt(k);
                        }
                    }
                    panel.IsOpened = false;
                    if (panel.CurrentProgressive != Panel.EProgressive.None && !mProgressives.Contains(stub))
                    {
                        mProgressives.Add(stub);
                    }
                    break;
                }
            }
            int id = 0;
            if (release || !found)
            {
                if (string.IsNullOrEmpty(uri))
                {
                    foreach (var kv in mPanelsLib)
                    {
                        if (kv.Value.panel == panel)
                        {
                            id = kv.Key;
                            break;
                        }
                    }
                }
                else
                {
                    id = uri.IgnoreCaseToHash();
                }
            }
            if (release)
            {
                Stub stub;
                if (mPanelsLib.Remove(id, out stub))
                {
                    stub.mNotUsed = true;
                    if (!found && stub.panel != null)
                    {
                        stub.panel.IsOpened = false;
                        if (stub.panel.CurrentProgressive != Panel.EProgressive.None && !mProgressives.Contains(stub))
                        {
                            mProgressives.Add(stub);
                        }
                    }
                    if (stub.panel == null || stub.panel.CurrentProgressive == Panel.EProgressive.None)
                        stub.Release();
                }
            }
            else if (!found)
            {
                Stub stub;
                if (mPanelsLib.TryGetValue(id, out stub) && stub.panel != null)
                {
                    stub.panel.IsOpened = false;
                    if (stub.panel.CurrentProgressive != Panel.EProgressive.None && !mProgressives.Contains(stub))
                    {
                        mProgressives.Add(stub);
                    }
                }
            }
        }

        public void ReleaseAllPanels()
        {
            if (!ParallelUnity.IsMainThread)
                throw new System.InvalidOperationException("Release panel can only be invoked from mainthread.");
            mPanels.Clear();
            mProgressives.Clear();
            for (int i = 0; i < mIntents.Count; i++)
            {
                mIntents[i].Cancel();
            }
            mIntents.Clear();
            foreach (var stub in mPanelsLib.Values)
            {
                if (stub.panel != null)
                {
                    stub.panel.IsFocused = false;
                    stub.panel.IsOpened = false;
                }
                stub.Release();
            }
            mPanelsLib.Clear();
        }

        // 窗口实例状态
        internal class Stub : ICommandHandler
        {
            string mUri;
            bool mIntentRequired;
            EPanelType mPanelType = (EPanelType)(-1);
            Panel mPanel;
            bool mIsReady;
            public string Uri => mUri;

            IAssetCache<GameObject> mAsset;

            internal bool mNotUsed;
            internal EPanelType panelType => mPanelType;
            internal bool isIntentRequired => mIntentRequired;
            internal Panel panel => mPanel;
            internal Canvas canvas => mPanel == null ? null : mPanel.GetCanvas();
            internal bool IsAssetReady
            {
                get
                {
                    if (!mIsReady && mAsset != null && mAsset.LoadResult != ELoadResult.Loading)
                    {
                        mIsReady = true;
                        var go = mAsset.LoadResult == ELoadResult.Success ? mAsset.Asset as GameObject : null;
                        var p = go == null ? null : go.GetComponent<Panel>();
                        if (p != null)
                        {
                            mPanelType = p.PanelType;
                            mIntentRequired = p is IPanelIntentRequired;
                        }
                    }
                    return mIsReady;
                }
            }
            internal bool IsInsidePanel => mUri.StartsWithIgnoreCase(InsidePrefix);
            public Stub(string uri)
            {
                this.mUri = uri;
            }

            public Stub(Panel insidePanel)
            {
                this.mUri = ParallelUtils.Concat(InsidePrefix, insidePanel.name);
                this.mIsReady = true;
                mPanel = insidePanel;
                mPanelType = insidePanel.PanelType;
                mIntentRequired = insidePanel is IPanelIntentRequired;
            }

            public ParallelDispatcher.Handler GetHandler()
            {
                return mAsset.GetParallelHandler();
            }

            internal void GetAsset()
            {
                if (!mIsReady && mAsset == null)
                {
                    mAsset = AssetsUtil.GetAssetAsync<GameObject>(mUri);
                    if (mAsset != null)
                    {
                        mAsset.QoS = EAssetQoS.Emergency;
                        mAsset.Retain();
                    }
                }
            }

            internal void Instantiate(PanelManager mgr)
            {
                if (mPanel != null || (int)mPanelType == -1 || mAsset == null)
                    return;
                var go = mAsset.Asset as GameObject;
                if (go == null)
                    return;
                var inst = GameObject.Instantiate(go, mgr.transform);
                mPanel = inst.GetComponent<Panel>();
                if (mPanel != null)
                {
                    inst.name = go.name;
                    InitPanelState(mgr);
                }
            }

            internal void InitPanelState(PanelManager mgr)
            {
                var canvas = mPanel.GetCanvas();
                canvas.worldCamera = mgr.m_UICamera;
                if (mgr.m_UICamera != null)
                    canvas.renderMode = RenderMode.ScreenSpaceCamera;
                if (mPanelType == EPanelType.Normal)
                {
                    canvas.sortingLayerID = mgr.NormalSortingLayer;
                    canvas.planeDistance = mgr.NormalDistance;
                    mgr.mOrderSortingLayers = true;
                }
                else if (mPanelType == EPanelType.Dialog)
                {
                    canvas.sortingLayerID = mgr.DialogSortingLayer;
                    canvas.planeDistance = mgr.DialogDistance;
                    mgr.mOrderSortingLayers = true;
                }
                else
                {
                    canvas.sortingLayerID = mgr.StatusSortingLayer;
                    canvas.planeDistance = mgr.StatusDistance;
                    canvas.sortingOrder = mPanel.StatusOrder;
                }
                //mPanel.ValidateVisible();
            }

            internal void Release()
            {
                if (mPanel != null)
                {
                    var go = mPanel.gameObject;
                    mPanel = null;
                    GlobalUtil.Destroy(go);
                }
                if (mAsset != null)
                {
                    var asset = mAsset;
                    mAsset = null;
                    asset.Release();
                }
            }

        }

        // 无 intent 调用参数
        class NotIntent
        {
            //internal static NotIntent NO_INTENT = new NotIntent();
            internal NotIntent() { }
        }

        // 打开窗口意图
        internal interface IIntentOperation : IYieldInstruction
        {
            Stub stub { get; }
            bool Execute(PanelManager mgr);
            void Cancel();
        }

        class Intent<T> : IIntentOperation
        {
            T mIntent;
            Stub mStub;
            bool mComplete;
            OpenPanelResult mRequest;
            public Stub stub => mStub;

            public bool YieldContinue()
            {
                return mComplete;
            }

            internal Intent(Stub panel, T intent, OpenPanelResult handler)
            {
                this.mStub = panel;
                this.mIntent = intent;
                mRequest = handler;
            }

            public bool IsIntent(T intent)
            {
                if (mIntent is System.IEquatable<T> eq)
                    return eq.Equals(intent);
                else if (mIntent == null)
                    return intent == null;
                else
                    return mIntent.Equals(intent);
            }

            public bool Execute(PanelManager mgr)
            {
                if (mIntent is NotIntent && mStub.isIntentRequired)
                {
                    Debug.LogError($"Can't open panel(\"{mStub.Uri}\") without panel intent");
                    mComplete = true;
                    return false;
                }
                mStub.Instantiate(mgr);
                if (mIntent is NotIntent && mStub.panel != null)
                {
                    mStub.panel.IsOpened = true;
                    mRequest?.Invoke(mStub.Uri, true, mStub.panel);
                    mComplete = true;
                    return true;
                }
                else if (mStub.panel is IPanelIntentHandler<T> handler)
                {
                    mStub.panel.Initialize();
                    if (handler.OnHandleIntent(mIntent))
                    {
                        mStub.panel.IsOpened = true;
                        mRequest?.Invoke(mStub.Uri, true, mStub.panel);
                    }
                    else
                    {
                        mStub.panel.ValidateVisible();
                        mRequest?.Invoke(mStub.Uri, false, null);
                    }
                    mComplete = true;
                    return true;
                }
                else
                {
                    mStub.panel.ValidateVisible();
                    mRequest?.Invoke(mStub.Uri, false, null);
                    mComplete = true;
                    return false;
                }
            }

            public void Cancel()
            {
                mRequest?.Invoke(mStub.Uri, false, null);
                mComplete = true;
            }

        }

    }
}
